import java.awt.Container;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class Window extends JFrame {
	
	public static DataFlavor f;
	static {
		try {
			f = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public class DraggablePort extends JPanel implements DropTargetListener{

		public DraggablePort(final String string) {
			super();
			setLayout(new GridLayout(7,7));
			setBorder(BorderFactory.createEtchedBorder());
			for (int i : new int[] {1,2,3}) {
				JLabel label = new Draggable(string + i);
				label.setBorder(BorderFactory.createEtchedBorder());
				add(label);
			}
			new DropTarget(this, this);
			
	
		}
		
		

		public void dragEnter(final DropTargetDragEvent dtde) {
		}

		public void dragExit(final DropTargetEvent dte) {
		}

		public void dragOver(final DropTargetDragEvent dtde) {
		}

		public void drop(final DropTargetDropEvent dtde) {
			Transferable t = dtde.getTransferable();
			try {
				final Integer i = (Integer) t.getTransferData(f);
				dtde.acceptDrop(DnDConstants.ACTION_MOVE);
				dtde.getDropTargetContext().dropComplete(true);
				final Draggable d = Draggable.map.get(i);
				EventQueue.invokeLater(new Runnable() {
					public void run() {
						Container c = d.getParent();
						if (c != null) {
							c.remove(d);
							c.validate();
							c.repaint();
						}
						add(d);
						validate();
						repaint();
					}
				});	
			} catch (UnsupportedFlavorException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}

		public void dropActionChanged(final DropTargetDragEvent dtde) {
		}

	}

	public static class Draggable extends JLabel implements DragGestureListener, DragSourceListener {

		private static Map<Integer, Draggable> map = new HashMap<Integer, Draggable>();
		private static int counter = 0;
		
		private DragSource dragSource;
		private int count;
	    
		public Draggable(final String string) {
			super(string);
			dragSource = new DragSource();
            dragSource.createDefaultDragGestureRecognizer(
                    this, DnDConstants.ACTION_MOVE, this);
            this.count = counter++;
            map.put(count, this);
            
		}	

		public void dragGestureRecognized(final DragGestureEvent dge) {
			dragSource.startDrag (dge, DragSource.DefaultMoveDrop, new TransferableInt(count), this);
			
		}

		public void dragDropEnd(final DragSourceDropEvent dsde) {
		}

		public void dragEnter(final DragSourceDragEvent dsde) {
		}

		public void dragExit(final DragSourceEvent dse) {
		}

		public void dragOver(final DragSourceDragEvent dsde) {
		}

		public void dropActionChanged(final DragSourceDragEvent dsde) {
		}
		
	}
	

	public Window() {
		super();
		setSize(300,300);
		setLayout(new GridLayout(2,2));
		add(new DraggablePort("G"));
		add(new DraggablePort("H"));
		add(new DraggablePort("I"));
		add(new DraggablePort("J"));
	}
	
	
	private static class TransferableInt implements Transferable {

		private int i;
		
		public TransferableInt(final int i) {
			this.i = i;
		}
		
		
		public Object getTransferData(final DataFlavor flavor)
				throws UnsupportedFlavorException, IOException {
			return i;
		}

		public DataFlavor[] getTransferDataFlavors() {
			return new DataFlavor[] {f};
		}

		public boolean isDataFlavorSupported(final DataFlavor flavor) {
			return f.equals(flavor);
		}
		
	}
	
	/**
	 * @param args
	 */
	public static void main(final String[] args) {
		Window w = new Window();
		w.setDefaultCloseOperation(Window.EXIT_ON_CLOSE);
		w.setVisible(true);

	}

}
